/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
Copyright (C) 2005 Stuart Dalton (badcdev@gmail.com)

This file is part of Quake III Arena source code.

Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.

Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Foobar; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
===========================================================================
*/

#include "client.h"
#include "snd_codec.h"
#include "snd_local.h"
#include "snd_public.h"

cvar_t         *s_volume;
cvar_t         *s_muted;
cvar_t         *s_musicVolume;
cvar_t         *s_doppler;
cvar_t         *s_backend;
cvar_t         *s_muteWhenMinimized;
cvar_t         *s_muteWhenUnfocused;

static soundInterface_t si;

/*
=================
S_ValidateInterface
=================
*/
static qboolean S_ValidSoundInterface(soundInterface_t * si)
{
	if(!si->Shutdown)
		return qfalse;
	if(!si->StartSound)
		return qfalse;
	if(!si->StartLocalSound)
		return qfalse;
	if(!si->StartBackgroundTrack)
		return qfalse;
	if(!si->StopBackgroundTrack)
		return qfalse;
	if(!si->RawSamples)
		return qfalse;
	if(!si->StopAllSounds)
		return qfalse;
	if(!si->ClearLoopingSounds)
		return qfalse;
	if(!si->AddLoopingSound)
		return qfalse;
	if(!si->AddRealLoopingSound)
		return qfalse;
	if(!si->StopLoopingSound)
		return qfalse;
	if(!si->Respatialize)
		return qfalse;
	if(!si->UpdateEntityPosition)
		return qfalse;
	if(!si->Update)
		return qfalse;
	if(!si->DisableSounds)
		return qfalse;
	if(!si->BeginRegistration)
		return qfalse;
	if(!si->RegisterSound)
		return qfalse;
	if(!si->ClearSoundBuffer)
		return qfalse;
	if(!si->SoundInfo)
		return qfalse;
	if(!si->SoundList)
		return qfalse;

#ifdef USE_VOIP
	if(!si->StartCapture)
		return qfalse;
	if(!si->AvailableCaptureSamples)
		return qfalse;
	if(!si->Capture)
		return qfalse;
	if(!si->StopCapture)
		return qfalse;
	if(!si->MasterGain)
		return qfalse;
#endif

	return qtrue;
}

/*
=================
S_StartSound
=================
*/
void S_StartSound(vec3_t origin, int entnum, int entchannel, sfxHandle_t sfx)
{
	if(si.StartSound)
	{
		si.StartSound(origin, entnum, entchannel, sfx);
	}
}

/*
=================
S_StartLocalSound
=================
*/
void S_StartLocalSound(sfxHandle_t sfx, int channelNum)
{
	if(si.StartLocalSound)
	{
		si.StartLocalSound(sfx, channelNum);
	}
}

/*
=================
S_StartBackgroundTrack
=================
*/
void S_StartBackgroundTrack(const char *intro, const char *loop)
{
	if(si.StartBackgroundTrack)
	{
		si.StartBackgroundTrack(intro, loop);
	}
}

/*
=================
S_StopBackgroundTrack
=================
*/
void S_StopBackgroundTrack(void)
{
	if(si.StopBackgroundTrack)
	{
		si.StopBackgroundTrack();
	}
}

/*
=================
S_RawSamples
=================
*/
void S_RawSamples(int stream, int samples, int rate, int width, int channels, const byte * data, float volume)
{
	if(si.RawSamples)
	{
		si.RawSamples(stream, samples, rate, width, channels, data, volume);
	}
}

/*
=================
S_StopAllSounds
=================
*/
void S_StopAllSounds(void)
{
	if(si.StopAllSounds)
	{
		si.StopAllSounds();
	}
}

/*
=================
S_ClearLoopingSounds
=================
*/
void S_ClearLoopingSounds(qboolean killall)
{
	if(si.ClearLoopingSounds)
	{
		si.ClearLoopingSounds(killall);
	}
}

/*
=================
S_AddLoopingSound
=================
*/
void S_AddLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
{
	if(si.AddLoopingSound)
	{
		si.AddLoopingSound(entityNum, origin, velocity, sfx);
	}
}

/*
=================
S_AddRealLoopingSound
=================
*/
void S_AddRealLoopingSound(int entityNum, const vec3_t origin, const vec3_t velocity, sfxHandle_t sfx)
{
	if(si.AddRealLoopingSound)
	{
		si.AddRealLoopingSound(entityNum, origin, velocity, sfx);
	}
}

/*
=================
S_StopLoopingSound
=================
*/
void S_StopLoopingSound(int entityNum)
{
	if(si.StopLoopingSound)
	{
		si.StopLoopingSound(entityNum);
	}
}

/*
=================
S_Respatialize
=================
*/
void S_Respatialize(int entityNum, const vec3_t origin, vec3_t axis[3], int inwater)
{
	if(si.Respatialize)
	{
		si.Respatialize(entityNum, origin, axis, inwater);
	}
}

/*
=================
S_UpdateEntityPosition
=================
*/
void S_UpdateEntityPosition(int entityNum, const vec3_t origin)
{
	if(si.UpdateEntityPosition)
	{
		si.UpdateEntityPosition(entityNum, origin);
	}
}

/*
=================
S_Update
=================
*/
void S_Update(void)
{
	if(s_muted->integer)
	{
		if(!(s_muteWhenMinimized->integer && com_minimized->integer) && !(s_muteWhenUnfocused->integer && com_unfocused->integer))
		{
			s_muted->integer = qfalse;
			s_muted->modified = qtrue;
		}
	}
	else
	{
		if((s_muteWhenMinimized->integer && com_minimized->integer) || (s_muteWhenUnfocused->integer && com_unfocused->integer))
		{
			s_muted->integer = qtrue;
			s_muted->modified = qtrue;
		}
	}

	if(si.Update)
	{
		si.Update();
	}
}

/*
=================
S_DisableSounds
=================
*/
void S_DisableSounds(void)
{
	if(si.DisableSounds)
	{
		si.DisableSounds();
	}
}

/*
=================
S_BeginRegistration
=================
*/
void S_BeginRegistration(void)
{
	if(si.BeginRegistration)
	{
		si.BeginRegistration();
	}
}

/*
=================
S_RegisterSound
=================
*/
sfxHandle_t S_RegisterSound(const char *sample)
{
	if(si.RegisterSound)
	{
		return si.RegisterSound(sample);
	}
	else
	{
		return 0;
	}
}

/*
=================
S_ClearSoundBuffer
=================
*/
void S_ClearSoundBuffer(void)
{
	if(si.ClearSoundBuffer)
	{
		si.ClearSoundBuffer();
	}
}

/*
=================
S_SoundInfo
=================
*/
void S_SoundInfo(void)
{
	if(si.SoundInfo)
	{
		si.SoundInfo();
	}
}

/*
=================
S_SoundList
=================
*/
void S_SoundList(void)
{
	if(si.SoundList)
	{
		si.SoundList();
	}
}


#ifdef USE_VOIP
/*
=================
S_StartCapture
=================
*/
void S_StartCapture(void)
{
	if(si.StartCapture)
	{
		si.StartCapture();
	}
}

/*
=================
S_AvailableCaptureSamples
=================
*/
int S_AvailableCaptureSamples(void)
{
	if(si.AvailableCaptureSamples)
	{
		return si.AvailableCaptureSamples();
	}
	return 0;
}

/*
=================
S_Capture
=================
*/
void S_Capture(int samples, byte * data)
{
	if(si.Capture)
	{
		si.Capture(samples, data);
	}
}

/*
=================
S_StopCapture
=================
*/
void S_StopCapture(void)
{
	if(si.StopCapture)
	{
		si.StopCapture();
	}
}

/*
=================
S_MasterGain
=================
*/
void S_MasterGain(float gain)
{
	if(si.MasterGain)
	{
		si.MasterGain(gain);
	}
}
#endif

//=============================================================================

/*
=================
S_Play_f
=================
*/
void S_Play_f(void)
{
	int             i;
	sfxHandle_t     h;
	char            name[256];

	if(!si.RegisterSound || !si.StartLocalSound)
	{
		return;
	}

	i = 1;
	while(i < Cmd_Argc())
	{
		if(!Q_strrchr(Cmd_Argv(i), '.'))
		{
			Com_sprintf(name, sizeof(name), "%s.wav", Cmd_Argv(1));
		}
		else
		{
			Q_strncpyz(name, Cmd_Argv(i), sizeof(name));
		}
		h = si.RegisterSound(name);
		if(h)
		{
			si.StartLocalSound(h, CHAN_LOCAL_SOUND);
		}
		i++;
	}
}

/*
=================
S_Music_f
=================
*/
void S_Music_f(void)
{
	int             c;

	if(!si.StartBackgroundTrack)
	{
		return;
	}

	c = Cmd_Argc();

	if(c == 2)
	{
		si.StartBackgroundTrack(Cmd_Argv(1), NULL);
	}
	else if(c == 3)
	{
		si.StartBackgroundTrack(Cmd_Argv(1), Cmd_Argv(2));
	}
	else
	{
		Com_Printf("music <musicfile> [loopfile]\n");
		return;
	}

}

/*
=================
S_Music_f
=================
*/
void S_StopMusic_f(void)
{
	if(!si.StopBackgroundTrack)
		return;

	si.StopBackgroundTrack();
}


//=============================================================================

/*
=================
S_Init
=================
*/
void S_Init(void)
{
	cvar_t         *cv;
	qboolean        started = qfalse;

	Com_Printf("------ Initializing Sound ------\n");

	s_volume = Cvar_Get("s_volume", "0.8", CVAR_ARCHIVE);
	s_musicVolume = Cvar_Get("s_musicvolume", "0.25", CVAR_ARCHIVE);
	s_muted = Cvar_Get("s_muted", "0", CVAR_ROM);
	s_doppler = Cvar_Get("s_doppler", "1", CVAR_ARCHIVE);
	s_backend = Cvar_Get("s_backend", "", CVAR_ROM);
	s_muteWhenMinimized = Cvar_Get("s_muteWhenMinimized", "0", CVAR_ARCHIVE);
	s_muteWhenUnfocused = Cvar_Get("s_muteWhenUnfocused", "0", CVAR_ARCHIVE);

	cv = Cvar_Get("s_initsound", "1", 0);
	if(!cv->integer)
	{
		Com_Printf("Sound disabled.\n");
	}
	else
	{

		S_CodecInit();

		Cmd_AddCommand("play", S_Play_f);
		Cmd_AddCommand("music", S_Music_f);
		Cmd_AddCommand("stopmusic", S_StopMusic_f);
		Cmd_AddCommand("s_list", S_SoundList);
		Cmd_AddCommand("s_stop", S_StopAllSounds);
		Cmd_AddCommand("s_info", S_SoundInfo);

		cv = Cvar_Get("s_useOpenAL", "1", CVAR_ARCHIVE);
		if(cv->integer)
		{
			//OpenAL
			started = S_AL_Init(&si);
			Cvar_Set("s_backend", "OpenAL");
		}

		if(!started)
		{
			started = S_Base_Init(&si);
			Cvar_Set("s_backend", "base");
		}

		if(started)
		{
			if(!S_ValidSoundInterface(&si))
			{
				Com_Error(ERR_FATAL, "Sound interface invalid.");
			}

			S_SoundInfo();
			Com_Printf("Sound initialization successful.\n");
		}
		else
		{
			Com_Printf("Sound initialization failed.\n");
		}
	}

	Com_Printf("--------------------------------\n");
}

/*
=================
S_Shutdown
=================
*/
void S_Shutdown(void)
{
	if(si.Shutdown)
	{
		si.Shutdown();
	}

	Com_Memset(&si, 0, sizeof(soundInterface_t));

	Cmd_RemoveCommand("play");
	Cmd_RemoveCommand("music");
	Cmd_RemoveCommand("stopmusic");
	Cmd_RemoveCommand("s_list");
	Cmd_RemoveCommand("s_stop");
	Cmd_RemoveCommand("s_info");

	S_CodecShutdown();
}
